﻿using System;
using System.Collections.Generic;
using System.Text;

namespace _2022Learning.StateMachine
{
    #region 状态机类
    /// <summary>
    /// 简单状态机对象类，以下我们描述的都是狭义的有限状态机
    /// Q：有限状态机与状态设计模式之间的关系？
    /// A：其实状态机是状态设计模式的一种实现，可以说状态机中运用到了状态设计模式，可能解释的有点模糊（捂脸）；
    /// 
    /// Q：怎么实现一个简单的状态机？或者是一个简单的状态机有哪些组成知识？
    /// A： 首相咱们得知道一个有限状态机有哪些组成条件，翻阅了一些资料之后，大致包含以下4要素
    /// 即现态、条件（事件）、动作、次态，单这4个要素并不是代码的组成部分，状态机程序在运行的时候，
    /// 或多或少能用这4要素定位出当前程序的状态，其中动作并不是必要的。
    /// 现态：程序对象现在所处的状态；
    /// 条件：引起状态变化的事件或者条件；
    /// 动作：状态变化时，触发的动作；
    /// 次态：程序对象状态变化后之后的当前状态；
    /// 
    /// Q：状态机的使用场景？
    /// A：知乎回答：理解就是有限种已确定的状态（已投入多少钱），根据不确定的输入（每次投面值不同的钱），在这几种状态中来回转换的场景；
    /// https://www.zhihu.com/question/31634405
    /// 
    /// 源码出处，有所改动 https://www.cnblogs.com/plateFace/p/5018686.html
    /// </summary>
    class StateMachine2
    {
        // mock程序所处状态
        private MachineState currentState = null;
        // mock程序变化前的状态
        private MachineState previousState = null;
        // mock引起变化的事件或条件
        private StateEvent dateEvent = null;
        public bool isStop { get; set; }

        public MachineState CurrentState => currentState;
        public MachineState PreviousState => previousState;
        /// <summary>
        /// 获取程序当前的状态信息
        /// </summary>
        /// <param name="state"></param>
        /// <returns></returns>
        private MachineState GetState(AnimationState state)
        {
            switch (state)
            {
                case AnimationState.Walk:
                    return new State_Walk();
                case AnimationState.Dead:
                    return new State_Dead();
            }
            return new State_Walk();
        }

        /// <summary>
        /// 改变状态
        /// </summary>
        /// <param name="animationState">状态机枚举</param>
        /// <param name="data">触发条件</param>
        /// <param name="previousData">先前触发条件</param>
        public void ChangeState(AnimationState animationState, StateEvent data, StateEvent previousData = null)
        {
            DoChangeState(GetState(animationState), data, previousData);
        }

        /// <summary>
        /// 具体执行状态改变
        /// </summary>
        /// <param name="state"></param>
        /// <param name="data"></param>
        /// <param name="previousData"></param>
        public void DoChangeState(MachineState state, StateEvent data, StateEvent previousData = null)
        {
            if (currentState != null && state.GetStateId == currentState.GetStateId)
            {
                return;
            }

            previousState = currentState;
            previousData = dateEvent;

            if (previousState != null)
            {
                previousState.Exit(previousData);
            }

            currentState = state;
            dateEvent = data;
            currentState.Enter(data);
        }

        /// <summary>
        /// 当前对象执行的功能，都委托给状态类来执行
        /// </summary>
        public void Update()
        {
            if (currentState == null)
            {
                Console.WriteLine("当前没有状态执行");
                return;
            }
            else if (isStop)
            {
                currentState.Exit(dateEvent);
                Console.WriteLine("状态机已停止");
                return;
            }
            else
            {
                currentState.Execute(dateEvent);
            }
        }
    }
    #endregion

    #region 状态机状态相关
    /// <summary>
    /// 状态机状态枚举
    /// </summary>
    public enum AnimationState
    {
        Walk = 1,
        Dead = 2
    }

    /// <summary>
    /// 状态基类，用于响应对象行为的改变
    /// 所有状态机的状态都需要继承重写此类
    /// </summary>
    abstract class MachineState
    {
        public virtual int GetStateId { get; }

        // 具体执行的行为
        public abstract void Enter(StateEvent Data);

        // 具体执行的行为
        public abstract void Execute(StateEvent Data);

        // 具体执行的行为
        public abstract void Exit(StateEvent Data);
    }

    /// <summary>
    /// 触发的事件
    /// </summary>
    class StateEvent
    {
        public string Date { get; set; }
    }

    /// <summary>
    /// 行走状态
    /// </summary>
    class State_Walk : MachineState
    {
        public const int Id = 1;

        public override int GetStateId => Id;
        public override void Enter(StateEvent Data)
        {
            Console.WriteLine("角色行走-进入" + Data.Date);
        }

        public override void Execute(StateEvent Data)
        {
            Console.WriteLine("角色行走-执行中" + Data.Date);
        }

        public override void Exit(StateEvent Data)
        {
            Console.WriteLine("角色行走-退出" + Data.Date);
        }
    }

    /// <summary>
    /// 死亡状态
    /// </summary>
    class State_Dead : MachineState
    {
        const int Id = 2;
        public override int GetStateId => Id;
        public override void Enter(StateEvent Data)
        {
            Console.WriteLine("角色死亡-进入" + Data.Date);
        }

        public override void Execute(StateEvent Data)
        {
            Console.WriteLine("角色死亡-执行中" + Data.Date);
        }

        public override void Exit(StateEvent Data)
        {
            Console.WriteLine("角色死亡-退出" + Data.Date);
        }
    }
    #endregion
}

